package com.heima.article.job;

import com.alibaba.fastjson.JSON;
import com.heima.article.service.HotArticleService;
import com.heima.common.exception.CustException;
import com.heima.model.common.constants.article.HotArticleConstants;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.model.mess.app.NewBehaviorDTO;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 从redis中获取数据
 * 进行文章算分
 */
@Component
@Slf4j
public class UpdateHotArticleJob {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private HotArticleService hotArticleService;

    @XxlJob("updateHotArticleJob")
    public ReturnT<String> updateHotArticleHandler(String param) {
        log.info("开始对文章实时算分.....");
        // 1. 获取redis 行为列表中待处理数据
        List<NewBehaviorDTO> behaviorList = getRedisBehaviorList();
        if (CollectionUtils.isEmpty(behaviorList)) {
            log.info("十秒钟都没人看，真是太冷清了，任务调度完成。。。");
            return ReturnT.SUCCESS;
        }
        //2.将数据按照文章分组 进行聚合统计  得到待更新的数据集合
           List<AggBehaviorDTO> aggBehaviorList  = getAggBehaviorList(behaviorList);
        if (CollectionUtils.isEmpty(aggBehaviorList)) {
            log.info("十秒钟都没人看，真是太冷清了，任务调度完成。。。");
            return ReturnT.SUCCESS;
        }
        //3.更新数据库的分值
        aggBehaviorList.forEach(hotArticleService::updateApArticle);

        log.info("结束对文章实时算分.....");
        return ReturnT.SUCCESS;
    }

    /**
     * 2.将数据聚合转换成一个实体类的统计数据
     * @return
     */
    private List<AggBehaviorDTO> getAggBehaviorList(List<NewBehaviorDTO> behaviorList) {
        //先将数据根据文章id进行分组 变成一个一个的集合
        Map<Long, List<NewBehaviorDTO>> map = behaviorList.stream().collect(Collectors.groupingBy(NewBehaviorDTO::getArticleId));
        //准备集合封装数据
        ArrayList<AggBehaviorDTO> list = new ArrayList<>();
        //对map在进行处理 对应的键和值
        map.forEach((articleId,newsBehaviorList)->{
            Optional<AggBehaviorDTO> reduceResult =
                    newsBehaviorList.stream().map(newBehavior -> {//对根据文章id分组的集合进行处理 转换成agg的对象
                AggBehaviorDTO aggBehaviorDTO = new AggBehaviorDTO();
                aggBehaviorDTO.setArticleId(articleId);
                //对行为进行处理
                switch (newBehavior.getType()) {
                    case LIKES:
                        aggBehaviorDTO.setLike(newBehavior.getAdd());
                        break;
                    case COLLECTION:
                        aggBehaviorDTO.setCollect(newBehavior.getAdd());
                        break;
                    case COMMENT:
                        aggBehaviorDTO.setComment(newBehavior.getAdd());
                        break;
                    case VIEWS:
                        aggBehaviorDTO.setComment(newBehavior.getAdd());
                        break;
                }
                return aggBehaviorDTO;
                //归并方法，将多个对象合并为一个对象输出
            }).reduce((a1, a2) -> {
                a1.setLike(a1.getLike() + a2.getLike());
                a1.setCollect(a1.getCollect() + a2.getCollect());
                a1.setComment(a1.getComment() + a2.getComment());
                a1.setView(a1.getView() + a2.getView());
                return a1;
            });
            //将从opentional中获取的对象使用get方法 添加到集合中
            list.add(reduceResult.get());
        });
        //返回集合
        return list;
    }

    /**
     * 1.从redis中获取缓存的行为数据
     * @return
     */
    private List<NewBehaviorDTO> getRedisBehaviorList() {
        try {
            //调用lua脚本并执行
            DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
            //设置脚本的返回结果
            redisScript.setResultType(List.class);
            //指定redis的lua脚本资源路径
            redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis.lua")));
            //radis执行lua脚本
            List<String> result = redisTemplate.execute(redisScript, Arrays.asList(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST));
            //将查询到的result转化为NesBehaviorDto对象 并储存到集合中
            return result.stream()
                    .map(jsonStr-> JSON.parseObject(jsonStr,NewBehaviorDTO.class))
                    .collect(Collectors.toList());
        } catch (Exception e) {
            e.printStackTrace();
            CustException.cust(AppHttpCodeEnum.SERVER_ERROR,"执行lua脚本错误");
        }

        return null;
    }

}
